View Javadoc
1 /* 2 * Title: S/MIME Project 3 * Description: S/MIME email sending capabilities 4 * @Author Vladimir Radisic 5 * @Version 2.0.1 6 */ 7 8 9 package org.webdocwf.util.smime.activation; 10 11 12 import org.webdocwf.util.smime.cms.*; 13 import org.webdocwf.util.smime.util.MimeAssist; 14 import org.webdocwf.util.smime.util.PFXUtils; 15 import org.webdocwf.util.smime.util.MimeAssist; 16 import org.webdocwf.util.smime.exception.SMIMEException; 17 import org.webdocwf.util.smime.exception.SMIMEIOException; 18 import javax.mail.internet.MimeMessage; 19 import javax.activation.DataSource; 20 import java.io.*; 21 import java.util.Vector; 22 import java.security.PrivateKey; 23 import java.security.KeyStore; 24 import java.security.cert.X509Certificate; 25 26 27 /*** 28 * CMSSignedDataSource represents implementation of DataSource interfaces. It is 29 * used within MimeMessage as a source of data. Also, object of this class is 30 * used to create DER encoded Cryptographic Message Syntax (CMS) object 31 * represented in ASN.1 notation according to RFC2630. This object (CMS) is used 32 * as the source of data for MimeMessage in the process of sending signed message. 33 */ 34 public class CMSSignedDataSource implements DataSource { 35 36 /*** 37 * Container for messages. 38 */ 39 private byte[] message; 40 41 /*** 42 * Container for SignerInfos. 43 */ 44 private SignerInfos sInfo; 45 46 /*** 47 * Certificates container. 48 */ 49 private Certificates certif; 50 51 /*** 52 * Number of certificates stored in certificates container. 53 */ 54 private int countCert = 0; 55 56 /*** 57 * Capabilities attributes and their order 58 */ 59 private String[] capabilities; 60 61 /*** 62 * Decision: external (true) / internal (false) signing 63 */ 64 private boolean externalSignature = true; 65 66 /*** 67 * Used in setting digestAlgorithms field in Signed Data sequence: 68 * {SHA1,MD2,MD5} 69 */ 70 private boolean[] typeOfDigest = { false, false, false }; 71 72 /*** 73 * Disables overlaping of capabilities for same group: 74 * (SYMMETRIC,SIGNATURE,ENCIPHER,DEFAULT) 75 */ 76 private boolean[] enableOfCapabil = { false, false, false, false }; 77 78 /*** 79 * Boundary used in process of generation external signed message. 80 */ 81 private String boundary = null; 82 83 /*** 84 * Constructs CMS object for signing with Mime Message in form of 85 * byte array and with given value for type of CMSSignedDataSource (type of signing). 86 * Type can be external or internal signing. 87 * @param message0 message for encryption 88 * @param externalSignature0 true = external signing, false = internal 89 * signing 90 * @exception SMIMEException in case of failure in MimeMessageConvertor 91 * class which performes transformation from MimeMessage object to byte array. 92 * Also, it can be caused by problems in construction or work with some 93 * inner objects instantiated from classes that belong to 94 * org.webdocwf.util.smime.der or org.webdocwf.util.smime.cms packages used 95 * in other CMSEnvelopedObject constructor. 96 */ 97 public CMSSignedDataSource(byte[] message0, boolean externalSignature0) throws SMIMEException { 98 message = message0; 99 externalSignature = externalSignature0; 100 if (externalSignature) 101 boundary = "smime_bondary_" + Long.toString(System.currentTimeMillis()); 102 sInfo = new SignerInfos(); 103 certif = new Certificates(); 104 capabilities = new String[18]; 105 } 106 107 /*** 108 * Constructs CMS object for signing with Mime Message in form of 109 * instance of MimeMessage class and with given value for type of CMSSignedDataSource 110 * (type of signing). Type can be external or internal signing. 111 * @param message0 message for encryption 112 * @param externalSignature0 true = external signing, false = internal 113 * signing 114 * @exception SMIMEException caused by problems in construction or work with 115 * some inner objects instantiated from classes that belong to 116 * org.webdocwf.util.smime.der or org.webdocwf.util.smime.cms packages used 117 * in other CMSEnvelopedObject constructor. 118 */ 119 public CMSSignedDataSource(MimeMessage message0, boolean externalSignature0) throws SMIMEException { 120 this(MimeAssist.messageConvertor(message0), externalSignature0); 121 } 122 123 /*** 124 * Sets Capabilities Attributes (method is optional, but if exists, must be 125 * performed before addSigner method). Depending on parameter type0, other five 126 * parameters make order in specific group of algorithms. Groups of algorithms 127 * with positions of specific algorithms are:<BR> 128 * (SIGNATURE, MD2 with RSA, MD5 with RSA, SHA1 with RSA, SHA1 with DSA, Unused field)<BR> 129 * (SYMMETRIC, RC2 40 bits, RC2 64 bits, RC2 128 bits, DES, DES_EDE3)<BR> 130 * (ENCIPHER, RSA, Unused field, Unused field, Unused field, Unused field)<BR> 131 * <BR> 132 * For example, if we wish to set Capabilities Attributes for symmetric algorithms 133 * in order: RC2 64 bits, RC2 40 bits and DES, encipher algorithm RSA (only possible 134 * in this version), and signature algorithms in order: SHA1 with RSA, MD5 with RSA 135 * and MD2 with RSA, we should make following lines of code<BR> 136 * <BR> 137 * setCapabilities ("SYMMETRIC", 2, 1, 0, 3, 0)<BR> 138 * setCapabilities ("ENCIPHER", 1, 0, 0, 0, 0)<BR> 139 * setCapabilities ("SIGNATURE", 3, 2, 1, 0, 0)<BR> 140 * <BR> 141 * 0 means exclusion of algorithm from the specified position in the method. It is 142 * free to decide which algorithm will be included, or which group of algorithm 143 * will be included in Capabilities Attributes. If no groups are added, capabilities 144 * attributes won't be added to Signed Attributes. If two or more signers will 145 * sign the message, and their capabilities are different, this method should 146 * be performed before every signing if we wish to specify Capabilities 147 * Attributes for all particular signers. If type0 parameter is set as:<BR> 148 * setCapabilities ("DEFAULT", 0, 0, 0, 0, 0)<BR> 149 * it is equivalent to:<BR> 150 * setCapabilities ("SYMMETRIC", 1, 0, 0, 0, 0)<BR> 151 * setCapabilities ("ENCIPHER", 0, 0, 1, 0, 0)<BR> 152 * setCapabilities ("SIGNATURE", 1, 0, 0, 0, 0)<BR> 153 * @param type0 sets group of algorithms for capabilities attributes. It can be set 154 * with values: SIGNATURE, SYMMETRIC, ENCIPHER or DEFAULT. 155 * @param par10 sets order in group of parameters, or exclude some algorithms 156 * from capabilities atributes. Can take values 1, 2, 3, 4 or 5 and 0 for 157 * exclusion of the particular algorithm. 158 * @param par20 same as for par10 159 * @param par30 same as for par10 160 * @param par40 same as for par10 161 * @param par50 same as for par10 162 * @exception SMIMEException if method is performed more than three times for one signer, 163 * or in case of wrong values of parameters. 164 */ 165 public void setCapabilities(String type0, int par10, int par20, int par30, int par40, int par50) throws SMIMEException { 166 if (par10 > 5 | par10 < 0 | par20 > 5 | par20 < 0 | par30 > 5 | par30 < 0 | par40 > 5 | par40 < 0 | par50 > 5 | par50 < 0) 167 throw new SMIMEException(this, 1028); 168 if (type0.equalsIgnoreCase("SYMMETRIC")) { 169 if (enableOfCapabil[0] == true | enableOfCapabil[3] == true) 170 throw new SMIMEException(this, 1029); 171 capabilities[par10] = "RC2_CBC_40"; // RC2 40 bits algorithm 172 capabilities[par20] = "RC2_CBC_64"; // RC2 64 bits algorithm 173 capabilities[par30] = "RC2_CBC_128"; // RC2 128 bits algorithm 174 capabilities[par40] = "DES"; // DES algorithm 175 capabilities[par50] = "DES_EDE3_CBC"; // DES_EDE3 algorithm 176 capabilities[0] = null; // Cleaning first element (this element is not important) 177 enableOfCapabil[0] = true; 178 } else if (type0.equalsIgnoreCase("SIGNATURE")) { 179 if (enableOfCapabil[1] == true | enableOfCapabil[3] == true) 180 throw new SMIMEException(this, 1029); 181 capabilities[par10 + 6] = "MD2_WITH_RSA"; // MD2 with RSA algorithm 182 capabilities[par20 + 6] = "MD5_WITH_RSA"; // MD5 with RSA algorithm 183 capabilities[par30 + 6] = "SHA1_WITH_RSA"; // SHA1 with RSA algorithm 184 capabilities[par40 + 6] = "SHA1_WITH_DSA"; // SHA1 with DSA algorithm 185 capabilities[par50 + 6] = null; // For future use 186 capabilities[6] = null; // Cleaning first element (this element is not important) 187 enableOfCapabil[1] = true; 188 } else if (type0.equalsIgnoreCase("ENCIPHER")) { 189 if (enableOfCapabil[2] == true | enableOfCapabil[3] == true) 190 throw new SMIMEException(this, 1029); 191 capabilities[par10 + 12] = "RSA"; // RSA Encription 192 capabilities[par20 + 12] = null; // For future use 193 capabilities[par30 + 12] = null; // For future use 194 capabilities[par40 + 12] = null; // For future use 195 capabilities[par50 + 12] = null; // For future use 196 capabilities[12] = null; // Cleaning first element (this element is not important) 197 enableOfCapabil[2] = true; 198 } else if (type0.equals("DEFAULT")) { 199 for (int i = 0; i != capabilities.length; i++) 200 capabilities[i] = null; 201 capabilities[13] = "RSA"; // RSA Encription 202 capabilities[9] = "SHA1_WITH_RSA"; // SHA1 with RSA algorithm 203 capabilities[1] = "RC2_CBC_40"; // RC2 40 bits algorithm 204 enableOfCapabil[3] = true; 205 } else 206 throw new SMIMEException(this, 1030); 207 } 208 209 /*** 210 * Adds Signer. This method must be performed at least once. 211 * @param pfx0 contains information from signer's .pfx or .p12 file 212 * @param includingCert0 true = automatically including all certificates from pfx0 213 * false = no certificate will be added 214 * @param includingSignAttrib0 true = signed attributes will be included, false 215 * = signed attributes will not be included 216 * @param signingAlg0 used for signing (can be SHA1_WITH_RSA, MD2_WITH_RSA, 217 * MD5_WITH_RSA or SHA1_WITH_DSA) 218 * @exception SMIMEException in case of wrong type of digest algorithm, or in 219 * case of problems with manipulation with .pfx or .p12 file in PFXUtils class. 220 * Also, it can be caused by problems in construction or work with some inner 221 * objects from org.webdocwf.util.smime.der or org.webdocwf.util.smime.cms package. 222 */ 223 public void addSigner(KeyStore pfx0, boolean includingCert0, boolean includingSignAttrib0, String signingAlg0) throws SMIMEException { 224 225 X509Certificate[] chain = PFXUtils.getCertificateChain(pfx0); 226 227 if (chain == null) 228 chain = PFXUtils.getAllX509Certificate(pfx0); 229 230 PrivateKey pKey = PFXUtils.getPrivateKey(pfx0); 231 232 this.addSigner(chain, pKey, includingCert0, includingSignAttrib0, signingAlg0); 233 } 234 235 /*** 236 * Adds Signer. This method must be performed at least once. 237 * @param chain0 signer's certificates chain. First certificate in chain 238 * must be owner's. 239 * @param privKey0 signer's private key (DSA or RSA depend on type of signing) 240 * @param includingCert0 true = automatically including all certificates from pfx0 241 * false = no certificate will be added 242 * @param includingSignAttrib0 true = signed attributes will be included, false 243 * = signed attributes will not be included 244 * @param signingAlg0 used for signing (can be SHA1_WITH_RSA, MD2_WITH_RSA, 245 * MD5_WITH_RSA or SHA1_WITH_DSA) 246 * @exception SMIMEException in case of wrong type of digest algorithm. Also, 247 * it can be caused by problems in construction or work with some inner 248 * objects from org.webdocwf.util.smime.der or org.webdocwf.util.smime.cms package. 249 */ 250 public void addSigner(X509Certificate[] chain0, PrivateKey privKey0, boolean includingCert0, boolean includingSignAttrib0, String signingAlg0) throws SMIMEException { 251 String digestAlg = null; 252 253 if (signingAlg0.equalsIgnoreCase("SHA1_WITH_RSA")) { 254 typeOfDigest[0] = true; 255 digestAlg = "SHA1"; 256 } else if (signingAlg0.equalsIgnoreCase("SHA1_WITH_DSA")) { 257 typeOfDigest[0] = true; 258 digestAlg = "SHA1"; 259 } else if (signingAlg0.equalsIgnoreCase("MD2_WITH_RSA")) { 260 typeOfDigest[1] = true; 261 digestAlg = "MD2"; 262 } else if (signingAlg0.equalsIgnoreCase("MD5_WITH_RSA")) { 263 typeOfDigest[2] = true; 264 digestAlg = "MD5"; 265 } else 266 throw new SMIMEException(this, 1031); 267 SignedAttributes attrib = null; // Signed attributes remain null if includingSignAttrib0==false 268 269 if (includingSignAttrib0 == true) // Creating signers info with signed attributes 270 { 271 attrib = new SignedAttributes(); // Creating container of signed attrinutes 272 ContentTypeAttribute cont = new ContentTypeAttribute("ID_DATA", "NAME_STRING"); 273 274 attrib.addSignedAttribute(cont.getDEREncoded()); 275 SigningTimeAttribute time = new SigningTimeAttribute(); 276 277 attrib.addSignedAttribute(time.getDEREncoded()); 278 boolean include = false; // Check for capabilities attributes existing 279 280 for (int i = capabilities.length; i != 0; i--) { 281 if (capabilities[i - 1] != null) { 282 include = true; 283 break; 284 } 285 } 286 if (include == true) { 287 CapabilitiesAttribute cap = new CapabilitiesAttribute(capabilities); 288 289 attrib.addSignedAttribute(cap.getDEREncoded()); 290 for (int i = capabilities.length; i != 0; i--) // resets capabilities 291 capabilities[i - 1] = null; 292 } 293 MessageDigestAttribute dig = new MessageDigestAttribute(message, digestAlg); 294 295 attrib.addSignedAttribute(dig.getDEREncoded()); 296 } 297 sInfo.addSigner(message, chain0[0], privKey0, attrib, signingAlg0); // First certificate in chain must be certificate asociated with owners of private key 298 if (includingCert0 == true) { 299 for (int i = 0; i != chain0.length; i++) // Adding certificates from certificate chain 300 { 301 certif.addCertificate(chain0[i]); 302 countCert++; 303 } 304 } 305 for (int i = 0; i != enableOfCapabil.length; i++) // reset to enable other combination of capabilities for other signers 306 enableOfCapabil[i] = false; 307 } 308 309 /*** 310 * Adds the Certificate 311 * @param cert0 X509 certificate 312 * @exception SMIMEException thrown in inner object which is instance of the class 313 * org.webdocwf.util.smime.cms.Certificates. 314 */ 315 public void addCertificate(X509Certificate cert0) throws SMIMEException { 316 certif.addCertificate(cert0); 317 countCert++; 318 } 319 320 /*** 321 * Returns complete DER encoded CMS Signed Object 322 * @return DER encoded CMS Signed Object represented as byte array 323 * @exception SMIMEException caused by problems in construction or dealing 324 * with some inner objects instantiated from classes that belong to 325 * org.webdocwf.util.smime.der or org.webdocwf.util.smime.cms packages. 326 */ 327 public byte[] getCMSSignedObject() throws SMIMEException { 328 SignedData signData = new SignedData(); // Container for signed data sub object 329 330 signData.addCMSVersion(new CMSVersion(1).getDEREncoded()); 331 DigestAlgorithmIdentifiers digAlg = new DigestAlgorithmIdentifiers(); 332 333 if (typeOfDigest[0] == true) 334 digAlg.addDigestAlgIdNullPar("SHA1", "NAME_STRING"); 335 if (typeOfDigest[1] == true) 336 digAlg.addDigestAlgIdNullPar("MD2", "NAME_STRING"); 337 if (typeOfDigest[2] == true) 338 digAlg.addDigestAlgIdNullPar("MD5", "NAME_STRING"); 339 signData.addDigestAlgorithm(digAlg.getDEREncoded()); 340 EncapsulatedContentInfo enc = new EncapsulatedContentInfo(); 341 ContentTypeIdentifier cont = new ContentTypeIdentifier("ID_DATA", "NAME_STRING"); 342 343 enc.addContentType(cont.getDEREncoded()); 344 if (externalSignature == false) { 345 enc.addEncapsulatedContent(message); 346 } 347 signData.addEncapsulatedContentInfo(enc.getDEREncoded()); 348 if (countCert != 0) 349 signData.addCertificate(certif.getDEREncoded()); 350 signData.addSignerInfos(sInfo.getDEREncoded()); 351 Content signedContent = new Content(signData.getDEREncoded(), true); // Filling signed data content in context specific DER object 352 ContentInfo cmsObjectSignedData = new ContentInfo(); 353 ContentTypeIdentifier contentTypeSignDataId = new ContentTypeIdentifier("ID_SIGNEDDATA", "NAME_STRING"); // Creating the Content Type 354 355 cmsObjectSignedData.addContentType(contentTypeSignDataId.getDEREncoded()); 356 cmsObjectSignedData.addContent(signedContent.getDEREncoded()); 357 return cmsObjectSignedData.getDEREncoded(); 358 } 359 360 /*** 361 * Returns complete DER encoded CMS Signed Object with BASE64 encoding 362 * @return DER encoded CMS Signed Object represented as byte array with 363 * performed BASE64 encoding. 364 * @exception SMIMEException in case of failure in Base64 encoding performed 365 * on the generated SMIME message byte array by method ofMimeAssist class. Also, 366 * it can be caused by problems in construction or work with some inner objects 367 * instantiated from classes that belong to org.webdocwf.util.smime.der or 368 * org.webdocwf.util.smime.cms packages used in getCMSSignedDataSource() method. 369 */ 370 public byte[] getBASE64CMSSignedObject() throws SMIMEException { 371 return MimeAssist.getBASE64WithBreakOn76(this.getCMSSignedObject()); 372 } 373 374 /*** 375 * Returns "micalg" parameter used in Content-Type of external signing 376 * @return String representation of micalg parameter. 377 */ 378 private String getMicalg() { 379 int sum = 0; 380 381 if (typeOfDigest[0]) 382 sum = sum + 1; 383 if (typeOfDigest[1]) 384 sum = sum + 10; 385 if (typeOfDigest[2]) 386 sum = sum + 100; 387 388 switch (sum) { 389 case 0: 390 return "\"unknown\""; 391 392 case 1: 393 return "\"sha1\""; 394 395 case 10: 396 return "\"md2\""; 397 398 case 100: 399 return "\"md5\""; 400 401 case 11: 402 return "\"sha1,md2\""; 403 404 case 101: 405 return "\"sha1,md5\""; 406 407 case 110: 408 return "\"md2,md5\""; 409 410 case 111: 411 return "\"sha1,md2,md5\""; 412 } 413 return "\"unknown\""; 414 } 415 416 /*** 417 * Returns composed message for external signing. 418 * @return External signed message represented as byte array. 419 * @exception SMIMEException in case of failure in Base64 encoding performed 420 * on the generated SMIME message byte array by method ofMimeAssist class. Also, 421 * it can be caused by problems in construction or work with some inner objects 422 * instantiated from classes that belong to org.webdocwf.util.smime.der or 423 * org.webdocwf.util.smime.cms packages used in getCMSSignedDataSource() method. 424 */ 425 private byte[] getExternalSignedMessage() throws SMIMEException { 426 427 try { 428 String extMessage = 429 "--" + boundary + "\r\n" + 430 new String(this.message, "ISO-8859-1") + "\r\n" + 431 "--" + boundary + "\r\n" + 432 "Content-Type: application/x-pkcs7-signature; name=smime.p7s" + "\r\n" + 433 "Content-Transfer-Encoding: base64" + "\r\n" + 434 "Content-Disposition: attachment; filename=smime.p7s" + "\r\n" + "\r\n" + 435 new String(this.getBASE64CMSSignedObject(), "ISO-8859-1") + "\r\n" + 436 "--" + boundary + "--"; 437 438 return extMessage.getBytes("ISO-8859-1"); 439 } catch (Exception e) { 440 throw SMIMEException.getInstance(this, e, "getExternalSignedMessage"); 441 } 442 } 443 444 /*** 445 * Implements getContentType method from DataSource interface 446 * @return Content-Type for MIME message header field 447 */ 448 public String getContentType() { 449 if (!externalSignature) 450 // For new version of mail clients: "application/pkcs7-mime; smime-type=signed-data; name=\"smime.p7m\"" 451 return "application/x-pkcs7-mime; smime-type=signed-data; name=\"smime.p7m\""; 452 else 453 // For new version of mail clients protocol: "application/pkcs7-signature; name=\"smime.p7s\"" 454 return "multipart/signed;" + "\r\n" + 455 " protocol=\"application/x-pkcs7-signature\";" + "\r\n" + 456 " micalg=" + this.getMicalg() + "; boundary=" + boundary; 457 458 } 459 460 /*** 461 * Implements getInputStream method from DataSource interface 462 * @return CMS signed object 463 * @exception SMIMEIOException thrown as result of SMIMEException 464 */ 465 public InputStream getInputStream() throws SMIMEIOException { 466 try { 467 if (!externalSignature) 468 return new ByteArrayInputStream(getCMSSignedObject()); 469 else 470 return new ByteArrayInputStream(getExternalSignedMessage()); 471 } catch (SMIMEException e) { 472 throw new SMIMEIOException(e); 473 } 474 } 475 476 /*** 477 * Implements getName method from DataSource interface 478 * @return Name: SignedDataContentInfo 479 */ 480 public String getName() { 481 return "SignedDataContentInfo"; 482 } 483 484 /*** 485 * Implements getOutputStream method from DataSource interface. This 486 * method is not in use. 487 * @return nothing 488 * @exception IOException is always thrown when this method is used. 489 */ 490 public OutputStream getOutputStream() throws IOException { 491 throw new IOException("SignedDataContentInfo does not support getOutputStream()"); 492 } 493 } 494

This page was automatically generated by Maven